package org.third.security.cert;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.third.common.utils.CodecUtils;
import org.third.common.utils.StreamUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import sun.misc.BASE64Encoder;
import sun.security.provider.X509Factory;

public class PemFileTest {
	static KeyFactory dsaKeyFactory = null;
	{
		try {
			dsaKeyFactory = KeyFactory.getInstance("RSA");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		try {
			new PemFileTest().testReadPkcs10();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void convertCrtToJks() {
		File crtFolder = new File(".");
		String folderFile = crtFolder.getAbsolutePath();

		FileInputStream crtFileStream = null;
		FileOutputStream jksFileStream = null;
		try {
			KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
			char[] password = "changeit".toCharArray();
			keystore.load(null, password);
			CertificateFactory cf = CertificateFactory.getInstance("X.509");

			Arrays.stream(crtFolder.listFiles())
					.filter(file -> "crt".equals(FilenameUtils.getExtension(file.getName()))).forEach(file -> {
						System.out.println("adding  " + file.getName() + " to truststore");
						InputStream tmpInputStream = null;
						try {
							tmpInputStream = new FileInputStream(file);
							Certificate certificate = cf.generateCertificate(tmpInputStream);
							keystore.setCertificateEntry(file.getName(), certificate);
						} catch (Exception e) {
							System.out.println(e.getMessage());
						} finally {
							IOUtils.closeQuietly(tmpInputStream);
						}
					});
			jksFileStream = new FileOutputStream(folderFile + ".jks");
			keystore.store(jksFileStream, password);
		} catch (Exception e) {
		}
	}

	@Test
	public void testP7b() throws Exception {
		
		//受信任证书链
        CertificateFactory factory = CertificateFactory.getInstance("X509");
        //构建用户证书和CertPath
        ByteArrayInputStream ins1 = new ByteArrayInputStream("message".getBytes());
        java.security.cert.X509Certificate usercert = (java.security.cert.X509Certificate) factory.generateCertificate(ins1);
        
        List<Certificate> certList = new ArrayList<Certificate>();
        certList.add(usercert);
        CertPath certPath = factory.generateCertPath(certList);
        CertPathValidator validator = CertPathValidator.getInstance("PKIX");
        
		// certifcate chain
		InputStream ins = PemFileTest.class.getResourceAsStream("/ssl/server.p7b");
		Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
		CertificateFactory certFactory = java.security.cert.CertificateFactory.getInstance("X509");
		Collection<Certificate> certs = (Collection<Certificate>) certFactory.generateCertificates(ins);
		for (Iterator i = certs.iterator(); i.hasNext();) {
			java.security.cert.X509Certificate certIt = (java.security.cert.X509Certificate) i.next();
			trustAnchors.add(new TrustAnchor(certIt, null));
		}
        
		PKIXParameters parameters = new PKIXParameters(trustAnchors);
		parameters.setRevocationEnabled(false);

		try {
			PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(certPath, parameters);
			TrustAnchor trustAnchor = result.getTrustAnchor();
			java.security.cert.X509Certificate issuer = trustAnchor.getTrustedCert();
			if (issuer == null) {
				issuer = usercert;
			}
			// 证书类型转换
			System.out.println("查询crl列表");
			boolean ok = true;
//			if (crlCache.getCheck() != CRLCache.CHECK_NO) {
//				ok = crlCache.check(libissuer, libCertificate);
//			usercert.get
				if (ok) {
					System.out.println("证书通过crl检测");
				} else {
					System.out.println("证书无效或者已被吊销");
					throw new Exception("证书无效或者已被吊销");
				}
//			}
			System.out.println("证书验证成功");
		} catch (CertPathValidatorException e) {
			System.out.println("证书验证失败");
			e.printStackTrace();
			throw e;
		} catch (InvalidAlgorithmParameterException e) {
			System.out.println("证书验证失败");
			e.printStackTrace();
		} catch (Exception e) {
			System.out.println("证书验证异常");
			throw e;
		}

	}

	@Test
	public void testReadPkcs10() throws Exception {
		InputStream ins = PemFileTest.class.getResourceAsStream("/ssl/server.crt");
//		byte[] encodedKey = CodecUtils.decodeBase64(StreamUtils.pkiStreamToString(ins));
		CertificateFactory certFactory = java.security.cert.CertificateFactory.getInstance("X509");
		java.util.List<java.security.cert.Certificate> certs = (List<Certificate>) certFactory
				.generateCertificates(ins);
		certs.forEach(c -> {
			System.out.println(c.getType() + "," + c);
			java.security.PublicKey publicKey = c.getPublicKey();
			System.out.println(publicKey.getFormat() + "," + publicKey.getAlgorithm());
		});

		java.security.cert.CertPath crtPath = certFactory.generateCertPath(certs);
		System.out.println(CodecUtils.encodeBase64String(crtPath.getEncoded()));
	}

	@Test
	public void testReadPkcs8() throws Exception {
		InputStream ins = PemFileTest.class.getResourceAsStream("/ssl/server_pkcs8.key");
		byte[] encodedKey = CodecUtils.decodeBase64(StreamUtils.pkiStreamToString(ins));
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
		java.security.PrivateKey privateKey = dsaKeyFactory.generatePrivate(pkcs8EncodedKeySpec);
	}

	public static String crtToString(Certificate certificate) {
		if (certificate != null) {
			BASE64Encoder encoder = new BASE64Encoder();
			StringBuffer stringBuffer = new StringBuffer();
			try {
				stringBuffer.append(X509Factory.BEGIN_CERT + "\n");
				stringBuffer.append(encoder.encode(certificate.getEncoded()));
				stringBuffer.append("\n" + X509Factory.END_CERT);
			} catch (CertificateEncodingException e) {
				stringBuffer.delete(0, stringBuffer.length());
			}
			return stringBuffer.toString();
		}
		return "";
	}

	private static final String PKCS8_BEGIN = "-----BEGIN PRIVATE KEY-----";
	private static final String PKCS8_END = "-----END PRIVATE KEY-----";
	private static final String PKCS1_BEGIN = "-----BEGIN RSA PRIVATE KEY-----";
	private static final String PKCS1_END = "-----END RSA PRIVATE KEY-----";

	public static String keyToString(PrivateKey privateKey) {
		String format = privateKey.getFormat();
		String begin = "";
		String end = "";
		if (format != null && (format.equalsIgnoreCase("PKCS#8") || format.equalsIgnoreCase("PKCS8"))) {
			begin = PKCS8_BEGIN;
			end = PKCS8_END;
		} else if (format != null && (format.equalsIgnoreCase("PKCS#1") || format.equalsIgnoreCase("PKCS1"))) {
			begin = PKCS1_BEGIN;
			end = PKCS1_END;
		} else {
			System.out.println("Not support type {} private key " + format);
			return "";
		}

		StringBuffer buffer = new StringBuffer();
		buffer.append(begin + "\n");
		BASE64Encoder encoder = new BASE64Encoder();
		buffer.append(encoder.encode(privateKey.getEncoded()));
		buffer.append("\n" + end + "\n");
		return buffer.toString();
	}

	@Test
	public void pfxToPrivateKeyAndCert() {
		InputStream inputStream = PemFileTest.class.getResourceAsStream("/ssl/server.p12");
		String passwd = "changeit";
		String keyPasswd = "changeit";

		PrivateKey privateKey = null;

		try {
			KeyStore ks = KeyStore.getInstance("pkcs12");
			ks.load(inputStream, passwd == null ? new char[0] : passwd.toCharArray());
			String alias = ks.aliases().nextElement();
			try {
				Certificate cert = ks.getCertificate(alias);
				privateKey = (PrivateKey) ks.getKey(alias, keyPasswd == null ? new char[0] : keyPasswd.toCharArray());
				System.out.println("Openssl p12: " + privateKey);
			} catch (Exception ex) {
				System.out.println("Resolve private key fail , the passwd is {} " + keyPasswd);
			}
			if (privateKey == null) {
				try {
					privateKey = (PrivateKey) ks.getKey(alias, passwd.toCharArray());
				} catch (Exception ex) {
					System.out.println("Resolve private key fail, the passwd is {}" + keyPasswd);
				}
			}
		} catch (Exception e) {
			System.out.println("convert pfx to PrivateKey fail , {}" + e.getMessage());
		}

	}
}
